Let's start by setting up the environment for simple tests. Create ModTest folder. Copy the Maps folder from this tutorial into the game folder. Start the the game on !ModTest! Ascension map. Save the game as ModTest. Now you'll quickly get into it when you need to test something out. The script in the map will run ModTest\Test.lua script whenever you click the right button. The file will be reloaded each time, so you won't need to restart the map when you change it. The Code folder contains Test.lua files that should be copied into ModTest folder.
Do you remember the Papyrus (!!QW)? It's an old custom quest log for ERM. In this tutorial we will test out creation of a prettier custom quest log and play with aligning rules.
So, QuestLog.pcx is the background picture. Size of the dialog is derived from the picture. On it we create a few controls, setting their coordinates explicitely. CloseDialog = true attribute of the button, naturally, makes it close the dialog when pressed. Note that HotKey = 28 stands for Enter key, as seen on HeroesKeys.png. Eventhough we've only created a few controls, you can see that coordinates calculation got a bit messy. In complex dialogs this may get far worse.
Now, to avoid dealing with coordinates directly each time we'll use horizontal Align control.
Margin is the space around its contents, SpaceX is the space between items in it.
ExpandWidth = 1 makes the text occupy all free space horizontally.
ExpandHeight = 1 makes it occupy the whole height of Align control,
which would be equal to height of the biggest control, Archer portrait.
The result of this step looks identical to Step 1 result.
Now we're gonna put a vertical list. For this we would put a number of horizontal Align controls into a vertical Align control.
Again, ExpandWidth = 1 on AlignH controls makes them occupy whole width of the list.
The content of AlignH controls is pretty random and serves to show different ways to align stuff.
The third AlignH control has AlignX = 0.5 attribute. It makes the contents centered horizontally.
You can also use AlignX = 1 value to align the contents to the right
or you can use any other value between 0 and 1.
Here I simply duplicated contents of the list and added Shaded = true attribute to the duplicates to make them distinct.
To make the list scroll I moved the scrollbar into ScrollBar attribute of the vertical Align control.
Scrolling is supported by the control out of the box.
Additionally, I added CatchKeys = true attribute to the scrollbar, so it reacts Up, Down, Page Up and Page Down keys.
One unfortunate thing is that you can't see part of the row at the bottom of the list, instead there's empty space.
You can set ShowPartialRow = true attribute for the vertical Align control to make that row visible.
To make it partially visible you can place a picture with the bottom part of the dialog on top of it.
Note: controls are placed in order in which they are added to the dialog,
i.e. each control is placed on top of controls that go before it.
As you know, IF:Q only offers a dialog in which you can choose between 2 options. In this tutorial we'll create a dialog for any number of options, specifically 3.
We start with something simple, just a row of 3 pictures, a caption and a button. The dialog itself has automatic border and again automatic size calculation works right for us.
Now we're making it prettier by adding captions, using proper font size and color and adding a box around the button.
The numbers between controls in AlignV add space between them. I've even removed SpaceY attribute, because all spaces are different.
The box aroung the button is placed using AlignLayers control. It places contained controls on top of each other.
AlignX = 0.5 and AlignX = 0.5 attributes place the button in the center of the box.
Not much to learn here, except the fact that we're using Pcx control the first time.
Here we add Cancel button which is there when AllowCancel variable is true. We use a few Align controls for this.
There are 2 ways to go. We could make one selection frame and move it between the pictures,
or we could use a separate frame for each of them, but show only the active frame.
I chose the letter because at first I wanted the frames to surround both picture and text, so
invisible frames played extra role of denoting the clickable area around each option.
As we want this test to become a library function in the end, we're making options creation dynamic.
choices table containing just the pictures is used as the input.
Caption is not a standard attribute, so we can give it our meaning, i.e. text under the pictue.
We loop through the pictures and populate the list with proper display.
Also note that frame color is taken from game.pal, as it is in the standard IF:Q dialog.
Adding the OnClick event to all frames that shows the selection frame and enables OK button.
Here we're adding right-click hints for the options. ShowLongHints = true makes the
dialog show LongHint of control on right click. When LongHint is nil, Hint is used instead.
The last thing is the result of future library function. If OK was clicked we'll return chosen option number, otherwise we'll return 0.
Show() method returns chosen item id, or 30721 if the dialog was closed by other means.
Note: 30722 and 30721 are standard OK and Cancel ids in the game, we use them for the sake of tradition.
In this tutorial we will create a context menu.
CheckGroup control would help us with the menu. Normally it's used to create a list of check boxes or radio buttons. We use check boxes with States = 1 to get them to act as buttons.
Context menus disappear when you click outside the menu or press Esc. We'll do it as well. Esc key is simple, the only important detail is that it has to go in OnKeyUp event instead of OnKeyDown, otherwise pressing Esc will close the dialog under ours if there is one. Clicking outside of the dialog is a bit tricky. Click events only fire when a dialog control is clicked. Custom dialogs have their area covered with control named "DialogArea", so clicks inside the dialog fire click events. To catch clicks outside of the dialog we create a transparent Area control, making sure it covers whole screen. The dialog size is then set manually to that of actual contents. In OnClick and OnRightClick events we close the dialog if the click occured outside of its area. We'll also add a caption, just in case.
To open the menu at the place of mouse click we use new reciever UX:M which gets us mouse coordinates. CheckGroup control sets ItemIndex property of contained check boxes (counting is done from 1, as always in Lua), so that's what we need as the result from context menu.
This step shows a scroll bar outside of Align control (CheckGroup is a descendant of Align control). When scroll bar is outside, you can assign its name to ScrollBar property. There's no use for scroll bar when there are 6 menu items and I don't think someone would need a menu with a lot of items, so this step is just educational.
Before I figured I can simply add a big invisible control to capture clicks outside of dialog area I tried another approach. Here we have an invisible dialog that occupies whole screen and manually created dialog box, layered under the content. This is a good example of Align controls use. It also demonstrates Tile property of Def and Pcx controls.